﻿using System;
using System.Drawing;

using Storm.TextEditor.Editor;
using Storm.TextEditor.Editor.Text;

namespace Storm.TextEditor.Parser.Objects
{
	/// <summary>
	/// Represents a range of rows that are formatted.
	/// </summary>
	public class FormatRange
	{
		#region Fields

		private Color backColor = Color.Empty;
		private Color foreColor = Color.Empty;
		private Color waveColor = Color.Empty;

		private object tag = null;

		private TextRange bounds    = new TextRange();
		private TextRange oldBounds = new TextRange();
		private SyntaxDocument document = null;
		
		#endregion

		#region Properties

		/// <summary>
		/// Gets or sets the background color of the FormatRange.
		/// </summary>
		public Color BackColor
		{
			get { return backColor; }
			set
			{
				backColor = value;
				this.Apply();
			}
		}

		/// <summary>
		/// Gets or sets the foreground color of the FormatRange.
		/// </summary>
		public Color ForeColor
		{
			get { return foreColor; }
			set
			{
				foreColor = value;
				this.Apply();
			}
		}

		/// <summary>
		/// Gets or sets the color of waves in the FormatRange.
		/// </summary>
		public Color WaveColor
		{
			get { return waveColor; }
			set
			{
				waveColor = value;
				this.Apply();
			}
		}

		/// <summary>
		/// Gets or sets the bounds of the FormatRange.
		/// </summary>
		public TextRange Bounds
		{
			get { return bounds; }
			set { bounds = value; }
		}

		/// <summary>
		/// Gets or sets the tag of the FormatRange.
		/// </summary>
		public object Tag
		{
			get { return tag; }
			set { tag = value; }
		}

		/// <summary>
		/// Gets or sets the document of the FormatRange.
		/// </summary>
		internal SyntaxDocument Document
		{
			get { return document; }
			set { document = value; }
		}

		#endregion

		#region Methods

		#region Public

		/// <summary>
		/// Returns whether the FormatRange's bounds contains the given TextPoint as an int.
		/// </summary>
		/// <param name="tp">TextPoint to check whether is contained in the FormatRange.</param>
		/// <returns>Whether the FormatRange's bounds contains the given TextPoint as an int.</returns>
		public int Contains(TextPoint tp)
		{
			return Contains(tp.X, tp.Y);
		}

		/// <summary>
		/// Returns whether the FormatRange's bounds contains the given coordinates as an int.
		/// </summary>
		/// <param name="x">X coordinate to check whether is contained in the FormatRange.</param>
		/// <param name="y">Y coordinate to check whether is contained in the FormatRange.</param>
		/// <returns>Whether the FormatRange's bounds contains the given coordinates as an int.</returns>
		public int Contains(int x, int y)
		{
			if (y < Bounds.FirstRow)
				return -1;

			if (y > Bounds.LastRow)
				return 1;

			if (y == Bounds.FirstRow && x < Bounds.FirstColumn)
				return -1;

			if (y == Bounds.LastRow && x > Bounds.LastColumn)
				return 1;

			return 0;
		}

		/// <summary>
		/// Tells the document of the FormatRange to reparse the row contained in the FormatRange.
		/// </summary>
		public void Apply()
		{
			if (this.Document == null)
				return;

			this.ApplyOld();
			for (int i = this.Bounds.FirstRow; i <= this.Bounds.LastRow; i++)
			{
				if (i < 0 || i >= this.Document.Count)
					return;

				Row row = this.Document[i];

				if (row == null)
					return;

				if (row.RowState == RowState.AllParsed)
					row.AddToParseQueue();
			}
		}

		/// <summary>
		/// Tells the document of the FormatRange to reparse the old rows contained in the FOrmatRange.
		/// </summary>
		public void ApplyOld()
		{
			for (int i = oldBounds.FirstRow; i <= oldBounds.LastRow; i++)
			{
				if (i < 0 || i >= Document.Count)
					return;

				Row row = this.Document[i];

				if (row == null)
					return;

				if (row.RowState == RowState.AllParsed)
					row.AddToParseQueue();
			}
		}

		#endregion

		#region Protected

		/// <summary>
		/// Updates the bounds according to the new given bounds.
		/// </summary>
		/// <param name="s">Sender object.</param>
		/// <param name="e">EventArgs.</param>
		protected virtual void BoundsChanged(object s, EventArgs e)
		{
			// Apply the new bounds.
			this.Apply();

			// Update the old bounds.
			oldBounds.FirstColumn = bounds.FirstColumn;
			oldBounds.LastColumn  = bounds.LastColumn;
			oldBounds.FirstRow    = bounds.FirstRow;
			oldBounds.LastRow     = bounds.LastRow;
		}

		#endregion

		#endregion

		/// <summary>
		/// Initializes a new instance of FormatRange.
		/// </summary>
		public FormatRange()
		{
			this.Bounds = new TextRange();
			this.Bounds.Change += new EventHandler(BoundsChanged);
		}

		/// <summary>
		/// Initializes a new instance of FormatRange.
		/// </summary>
		public FormatRange(TextRange bounds, Color waveColor)
		{
			this.WaveColor = waveColor;

			this.Bounds = bounds;
			this.Bounds.Change += new EventHandler(BoundsChanged);
		}

		/// <summary>
		/// Initializes a new instance of FormatRange.
		/// </summary>
		public FormatRange(TextRange bounds, Color foreColor, Color backColor)
		{
			this.BackColor = backColor;
			this.ForeColor = foreColor;

			this.Bounds = bounds;
			this.Bounds.Change += new EventHandler(BoundsChanged);
		}

		/// <summary>
		/// Initializes a new instance of FormatRange.
		/// </summary>
		public FormatRange(TextRange bounds, Color foreColor, Color backColor, Color waveColor)
		{
			this.BackColor = backColor;
			this.ForeColor = foreColor;
			this.WaveColor = waveColor;

			this.Bounds = bounds;
			this.Bounds.Change += new EventHandler(BoundsChanged);
		}
	}
}
